package nape.geom;
import zpp_nape.Config;
import zpp_nape.Const;
import zpp_nape.util.Flags;
import DummyNapeMain;
import zpp_nape.util.Math;
import zpp_nape.util.Names;
import zpp_nape.util.Pool;
import zpp_nape.util.Lists;
import zpp_nape.shape.Circle;
import zpp_nape.shape.Edge;
import zpp_nape.space.Space;
import zpp_nape.shape.Polygon;
import zpp_nape.shape.Shape;
import zpp_nape.phys.FeatureMix;
import zpp_nape.phys.FluidProperties;
import zpp_nape.phys.Material;
import zpp_nape.phys.Body;
import zpp_nape.geom.AABB;
import zpp_nape.geom.Distance;
import zpp_nape.geom.Mat23;
import zpp_nape.geom.Vec2;
import zpp_nape.geom.VecMath;
import zpp_nape.geom.Collide;
import zpp_nape.dynamics.Arbiter;
import zpp_nape.dynamics.Contact;
import zpp_nape.dynamics.InteractionFilter;
import zpp_nape.constraint.Constraint;
import zpp_nape.dynamics.SpaceArbiterList;
import zpp_nape.constraint.Matrices;
import zpp_nape.constraint.PivotJoint;
import zpp_nape.constraint.WeldJoint;
import nape.util.Flags;
import nape.util.Lists;
import nape.util.Debug;
import nape.space.Space;
import nape.shape.Circle;
import nape.shape.Polygon;
import nape.shape.Edge;
import nape.shape.Shape;
import nape.phys.FluidProperties;
import nape.phys.Material;
import nape.phys.Body;
import nape.geom.AABB;
import nape.geom.Geom;
import nape.geom.Vec2;
import nape.dynamics.Contact;
import nape.dynamics.Arbiter;
import nape.dynamics.InteractionFilter;
#if swc@:keep#end class Mat23{
    public var zpp_inner:ZPP_Mat23;
    public var a(flibget_a,flibset_a):Float;
    inline function flibget_a()return zpp_inner.a inline function flibset_a(a:Float){
        if(({
            #if flash9 var ret:Bool=untyped __global__["isNaN"](a);
            ret;
            #else Math.isNaN(a);
            #end
        }))throw "Error: Mat23::"+"a"+" cannot be NaN";
        return zpp_inner.a=a;
    }
    public var b(flibget_b,flibset_b):Float;
    inline function flibget_b()return zpp_inner.b inline function flibset_b(b:Float){
        if(({
            #if flash9 var ret:Bool=untyped __global__["isNaN"](b);
            ret;
            #else Math.isNaN(b);
            #end
        }))throw "Error: Mat23::"+"b"+" cannot be NaN";
        return zpp_inner.b=b;
    }
    public var tx(flibget_tx,flibset_tx):Float;
    inline function flibget_tx()return zpp_inner.tx inline function flibset_tx(tx:Float){
        if(({
            #if flash9 var ret:Bool=untyped __global__["isNaN"](tx);
            ret;
            #else Math.isNaN(tx);
            #end
        }))throw "Error: Mat23::"+"tx"+" cannot be NaN";
        return zpp_inner.tx=tx;
    }
    public var c(flibget_c,flibset_c):Float;
    inline function flibget_c()return zpp_inner.c inline function flibset_c(c:Float){
        if(({
            #if flash9 var ret:Bool=untyped __global__["isNaN"](c);
            ret;
            #else Math.isNaN(c);
            #end
        }))throw "Error: Mat23::"+"c"+" cannot be NaN";
        return zpp_inner.c=c;
    }
    public var d(flibget_d,flibset_d):Float;
    inline function flibget_d()return zpp_inner.d inline function flibset_d(d:Float){
        if(({
            #if flash9 var ret:Bool=untyped __global__["isNaN"](d);
            ret;
            #else Math.isNaN(d);
            #end
        }))throw "Error: Mat23::"+"d"+" cannot be NaN";
        return zpp_inner.d=d;
    }
    public var ty(flibget_ty,flibset_ty):Float;
    inline function flibget_ty()return zpp_inner.ty inline function flibset_ty(ty:Float){
        if(({
            #if flash9 var ret:Bool=untyped __global__["isNaN"](ty);
            ret;
            #else Math.isNaN(ty);
            #end
        }))throw "Error: Mat23::"+"ty"+" cannot be NaN";
        return zpp_inner.ty=ty;
    }
    public function new(?a:Float=1.0,?b:Float=0.0,?c:Float=0.0,?d:Float=1.0,?tx:Float=0.0,?ty:Float=0.0){
        zpp_inner=ZPP_Mat23.get();
        this.a=a;
        this.b=b;
        this.tx=tx;
        this.c=c;
        this.d=d;
        this.ty=ty;
    }
    #if flash9#if swc@:keep#end public static inline function fromMatrix(m:flash.geom.Matrix){
        return new Mat23(m.a,m.b,m.c,m.d,m.tx,m.ty);
    }
    #end#if swc@:keep#end public static inline function rotation(angle:Float){
        var cos=Math.cos(angle);
        var sin=Math.sin(angle);
        return new Mat23(cos,-sin,sin,cos,0,0);
    }
    #if swc@:keep#end public static inline function translation(tx:Float,ty:Float){
        return new Mat23(1,0,0,1,tx,ty);
    }
    #if swc@:keep#end public static inline function scale(sx:Float,?sy:Float){
        if(({
            #if flash9 var ret:Bool=untyped __global__["isNaN"](sy);
            ret;
            #else Math.isNaN(sy);
            #end
        }))sy=sx;
        return new Mat23(sx,0,0,sy,0,0);
    }
    #if swc@:keep#end public inline function copy()return new Mat23(a,b,c,d,tx,ty)public var determinant(flibget_determinant,never):Float;
    inline function flibget_determinant(){
        return a*d-b*c;
    }
    #if swc@:keep#end public inline function singular(){
        var d=determinant;
        return d*d<ZPP_Const.EPSILON;
    }
    #if swc@:keep#end public inline function inverse(){
        var det=determinant;
        if(det*det<ZPP_Const.EPSILON)throw "Error: Matrix is singular and cannot be inverted";
        det=1.0/det;
        return new Mat23(d*det,-b*det,-c*det,a*det,(b*ty-d*tx)*det,(c*tx-a*ty)*det);
    }
    #if swc@:keep#end public inline function transpose()return new Mat23(a,c,b,d,tx,ty)#if swc@:keep#end public inline function concat(x:Mat23){
        return new Mat23(x.a*a+x.b*c,x.a*b+x.b*d,x.c*a+x.d*c,x.c*b+x.d*d,x.a*tx+x.b*ty+x.tx,x.c*tx+x.d*ty+x.ty);
    }
    #if swc@:keep#end public inline function transform(p:Vec2):Vec2{
        return new Vec2(p.x*a+p.y*b+tx,p.x*c+p.y*d+ty);
    }
    @:keep public inline function toString(){
        return "{ a: "+a+" b: "+b+" c: "+c+" d: "+d+" tx: "+tx+" ty: "+ty+" }";
    }
    #if swc@:keep#end public inline function equiorthogonal(){
        if(singular())return false;
        else{
            var x=a*b+c*d;
            if(x*x<ZPP_Const.EPSILON){
                var y=a*a+b*b-c*c-d*d;
                return y*y<ZPP_Const.EPSILON;
            }
            else return false;
        }
    }
    #if swc@:keep#end public inline function equiorthogonalise(){
        if(!equiorthogonal()){
            var k1=Math.sqrt(a*a+c*c);
            var k2=Math.sqrt(b*b+d*d);
            if(k1*k1<ZPP_Const.EPSILON||k2*k2<ZPP_Const.EPSILON)throw "Error: Matrix is singular and cannot be "+"equiorthogonal"+"ised";
            var k=(k1+k2)/2;
            k1=k/k1;
            k2=k/k2;
            a*=k1;
            c*=k1;
            b*=k2;
            d*=k2;
            var dot=a*b+c*d;
            var ang=0.25*Math.PI-0.5*Math.acos(dot/(k*k));
            if(determinant>0)ang=-ang;
            var sin=Math.sin(ang);
            var cos=Math.cos(ang);
            var a2=a*cos-c*sin;
            var b2=b*cos+d*sin;
            c=c*cos+a*sin;
            a=a2;
            d=d*cos-b*sin;
            b=b2;
        }
        return this;
    }
    #if swc@:keep#end public inline function orthogonal(){
        var x=a*b+c*d;
        if(x*x<ZPP_Const.EPSILON){
            var y=a*a+b*b-1;
            var z=c*c+d*d-1;
            return y*y<ZPP_Const.EPSILON&&z*z<ZPP_Const.EPSILON;
        }
        else return false;
    }
    #if swc@:keep#end public inline function orthogonalise(){
        if(!orthogonal()){
            var k1=Math.sqrt(a*a+c*c);
            var k2=Math.sqrt(b*b+d*d);
            if(k1*k1<ZPP_Const.EPSILON||k2*k2<ZPP_Const.EPSILON)throw "Error: Matrix is singular and cannot be "+"orthogonal"+"ised";
            var k=1;
            k1=k/k1;
            k2=k/k2;
            a*=k1;
            c*=k1;
            b*=k2;
            d*=k2;
            var dot=a*b+c*d;
            var ang=0.25*Math.PI-0.5*Math.acos(dot/(k*k));
            if(determinant>0)ang=-ang;
            var sin=Math.sin(ang);
            var cos=Math.cos(ang);
            var a2=a*cos-c*sin;
            var b2=b*cos+d*sin;
            c=c*cos+a*sin;
            a=a2;
            d=d*cos-b*sin;
            b=b2;
        }
        return this;
    }
}
